
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {

    private static Map<String,JavaToSql> ALL_TYPE = new HashMap<>();
    static {
        ALL_TYPE.put("String",new JavaToSql("String","VARCHAR","VARCHAR","(255)"));
        ALL_TYPE.put("Long",new JavaToSql("Long","BIGINT","BIGINT","(20)"));
        ALL_TYPE.put("long",new JavaToSql("long","BIGINT","BIGINT","(20)"));
        ALL_TYPE.put("int",new JavaToSql("int","INT","INTEGER","(11)"));
        ALL_TYPE.put("Integer",new JavaToSql("Integer","INT","INTEGER","(11)"));
        ALL_TYPE.put("Date",new JavaToSql("Date","DATETIME","TIMESTAMP",""));
        ALL_TYPE.put("Boolean",new JavaToSql("Boolean","BIT","BIT",""));
        ALL_TYPE.put("boolean",new JavaToSql("Boolean","BIT","BIT",""));
    }

    private static class JavaToSql{
        JavaToSql(String type,String sql,String mybaties,String len){
            this.type = type;
            this.sql = sql;
            this.mybaties = mybaties;
            this.len = len;
        }
        private String type;
        private String sql;
        private String mybaties;
        private String len;
    }

    public static void main(String [] args) throws Exception{
        Dir dir = new Dir("D:/WORK/test-project","mydemo");
        for(String item : dir.toDirs()){
            File fitem = new File(item);
            if(!fitem.exists()){
                fitem.mkdirs();
            }
        }
        for(FileTemp item : dir.toFile()){
            createFile(item);
        }
        createTable(dir,new Table("test_table","TestTable","test",getColumns("name String","age Long","email String","phone_name String")));
        createTable(dir,new Table("operator","Operator","sys",getColumns("user_name String 用户昵称","pwd String 用户密码","status Integer 状态")));
        System.out.println("OK");
    }

    public static Column [] getColumns(String... str){
        Column [] clos = new Column[str.length + 3];
        clos[0] = getColumn("id Long 主键");
        for(int i = 0; i < str.length; i ++){
            clos[i +1] = getColumn(str[i]);
        }
        clos[str.length +1] = getColumn("create_date Date 添加日期");
        clos[str.length +2] = getColumn("modify_date Date 最后修改日期");
        return clos;
    }

    private static Column getColumn(String colunStr){
        String [] item = colunStr.split(" ");
        String desc = "";
        if(item.length == 3){
            desc = item[2];
        }
        return new Column(item[0],item[1],desc);
    }

    private static class Dir{
        public Dir(String dir,String name){
            this.root = dir;
            this.name = name;
            this.java = this.root + "/" + name +"/src/main/java/com/"+name.toLowerCase();
            this.sources = this.root +"/" + name+"/src/main/resources";
            this.project = this.root +"/" + name;
        }
        private String root;
        private String java;
        private String sources;
        private String project;
        private String name;
        public List<String> toDirs(){
            List<String> result = new ArrayList<String>();
            result.add(this.root);
            result.add(this.project);
            result.add(this.java);

            result.add(this.java+"/dao");
            result.add(this.java +"/model");
            result.add(this.java+"/service");
            result.add(this.java+"/controller");
            result.add(this.java+"/config");
            result.add(this.java+"/config/interceptor");
            result.add(this.java+"/common");
            result.add(this.java+"/util");
            result.add(this.sources);
            result.add(this.sources+"/mapper");
            return  result;
        }

        public List<FileTemp> toFile(){
            List<FileTemp> result = new ArrayList<FileTemp>();

            String fileChar = this.name.substring(0,1).toUpperCase();
            String nameLast = this.name.substring(1);

            Placeholder name = new Placeholder("project",this.name);
            Placeholder cname = new Placeholder("className",fileChar+nameLast);

            result.add(new FileTemp(this.java+"/"+fileChar.toUpperCase()+nameLast+"Application.java","application.java-1",name,cname));
            result.add(new FileTemp(this.sources+"/application.properties","application.properties",name));
            result.add(new FileTemp(this.project+"/pom.xml","pom.xml-v1.0",name));
            result.add(new FileTemp(this.sources+"/logback.xml","logback.xml",name));
            result.add(new FileTemp(this.sources+"/mybatis-config.xml","mybatis-config.xml",name));
            result.add(new FileTemp(this.sources+"/spring-all.xml","sprint-all.xml",name));
            result.add(new FileTemp(this.java+"/common/ResData.java","ResData.java-1",name));
            result.add(new FileTemp(this.java+"/common/ControllerContent.java","ControllerContent.java",name));
            result.add(new FileTemp(this.java+"/common/UserAuthorization.java","UserAuthorization.java-1",name));
            result.add(new FileTemp(this.java+"/config/interceptor/CommonInterceptor.java","CommonInterceptor.java-1",name));
            result.add(new FileTemp(this.java+"/config/BeanConfig.java","BeanConfig.java",name));
            result.add(new FileTemp(this.java+"/config/DruidStatFilter.java","DruidStatFilter.java",name));
            result.add(new FileTemp(this.java+"/config/DruidStatViewServlet.java","DruidStatViewServlet.java",name));
            result.add(new FileTemp(this.java+"/config/GlobalExceptionHandler.java","GlobalExceptionHandler.java",name));
            result.add(new FileTemp(this.java+"/service/CacheService.java","CacheService.java",name));
            result.add(new FileTemp(this.java+"/util/CacheKeyEnum.java","CacheKeyEnum.java",name));
            result.add(new FileTemp(this.java+"/util/CacheKeysUtils.java","CacheKeysUtils.java",name));
            result.add(new FileTemp(this.java+"/util/MD5Utils.java","MD5Utils.java",name));
            result.add(new FileTemp(this.java+"/util/CookieUtils.java","CookieUtils.java-1",name));
            result.add(new FileTemp(this.java+"/util/CookieKey.java","CookieKey.java-1",name));
            result.add(new FileTemp(this.java+"/util/Utils.java","Utils.java",name));
            result.add(new FileTemp(this.java+"/util/PasswordUtils.java","PasswordUtils.java",name));
            result.add(new FileTemp(this.java+"/util/AES.java","AES.java",name));
            result.add(new FileTemp(this.java+"/util/ErrorCode.java","ErrorCode.java",name));

            result.add(new FileTemp(this.java+"/controller/BaseController.java","BaseController.java",name));
            result.add(new FileTemp(this.java+"/controller/IndexController.java","IndexController.java",name));
            return result;
        }
    }

    private static class FileTemp{
        FileTemp(String path,String fileName,Placeholder... holder){
            this.path = path;
            this.fileName = fileName;
            this.holder = holder;
        }
        private String path;
        private String fileName;
        private Placeholder [] holder;
    }

    private static class Placeholder{
        Placeholder(String key,String value){
            this.key = key;
            this.value = value;
        }
        private String key;
        private String value;
    }

    public static void createFile(FileTemp tmp) throws Exception{
        System.out.println("path:"+tmp.path);
        File to = new File(tmp.path);
        if(!to.exists()){
            to.createNewFile();
        }
        String url = getFileTemplateUrl(tmp.fileName);
        String content = getFileTemplateValue(url);
        if(tmp.holder != null && tmp.holder.length > 0){
            for(Placeholder item : tmp.holder){
                content = replaseAll(content,item); //  content.replaceAll(tt,projectName);
            }
        }
        wirteFile(to,content);
    }

    private static void wirteFile(File to,String content) throws Exception{
        File parent = new File(to.getParent());
        if(!parent.exists()){
            parent.mkdirs();
        }
        System.out.println(to.getAbsolutePath());
        if(!to.exists()){
            to.createNewFile();
        }

        BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(to));
        bs.write(content.getBytes("utf-8"));
        bs.close();
    }

    private static class Table{
        Table(String name,String className,String path,Column... column){
            this.path = path;
            this.name = name;
            this.className = className;
            this.column = column;
        }
        private String path;
        private String name;    // 表名字
        private String className; // 类名
        private Column [] column; // 例如：nickname String  role_id Long
    }

    private static class Column{
        Column(String name,String type,String desc){
            this.name = name;
            this.type = type;
            this.desc = desc;
        }
        private String name;
        private String type;
        private String desc;
    }

    private static void createTable(Dir dir,Table... table) throws Exception{
        if(table == null || table.length == 0){
            return;
        }
        for(Table item : table){
            createModel(dir,item);
            createDaoAndService(dir,item);
            createMapperXml(dir,item);
        }
    }

    private static void createDaoAndService(Dir dir,Table table) throws Exception{
        String className = table.className+"Mapper";
        StringBuilder result = new StringBuilder();
        result.append("package com.").append(dir.name).append(".dao."+table.path+";\r\n\r\n");

        result.append("import com."+dir.name+".model."+table.path+"."+table.className+";\r\n");
        result.append("import org.apache.ibatis.annotations.Mapper;\r\n\r\n");
        result.append("import java.util.List;\r\n\r\n");

        result.append("@Mapper\r\n");
        result.append("public interface  "+className+" {\r\n\r\n");

        result.append("\t List<"+table.className+"> find("+table.className+" search);\r\n\r\n");
        result.append("\t int save("+table.className+" model);\r\n\r\n");
        result.append("\t int update("+table.className+" update);\r\n\r\n");

        result.append("}").append("\r\n");
        File ja = new File(dir.java+"/dao/"+table.path+"/"+className+".java");
        wirteFile(ja,result.toString());

        // --------------------------
        StringBuilder constants = new StringBuilder();
        constants.append("package com."+dir.name+".common;\r\n\r\n");
        constants.append("public final class Constants {\r\n\r\n");
        constants.append("\tpublic static final int DEFAULT_AUTOCOMPLATE_SIZE = 20;\r\n");
        constants.append("}\r\n");
        File constantsFile = new File(dir.java+"/common/Constants.java");
        wirteFile(constantsFile,constants.toString());


        // --------------------------
        StringBuilder baseService = new StringBuilder();
        baseService.append("package com.").append(dir.name).append(".service;\r\n\r\n");
        baseService.append("import com."+dir.name+".common.Constants;\r\n");
        baseService.append("import com.github.pagehelper.PageHelper;\r\n\r\n");

        baseService.append("public class BaseService {\r\n\r\n");
        baseService.append("\tprotected void setPage(int currPage,int pageSize){\r\n");
        baseService.append("\t\tcurrPage = currPage <= 0 ? 1: currPage;\r\n");
        baseService.append("\t\tpageSize = pageSize <= 0 ? Constants.DEFAULT_AUTOCOMPLATE_SIZE:pageSize;\r\n");
        baseService.append("\t\tPageHelper.startPage(currPage,pageSize);\r\n");
        baseService.append("\t}\r\n");
        baseService.append("}\r\n");
        File baseServiceFile = new File(dir.java+"/service/BaseService.java");
        wirteFile(baseServiceFile,baseService.toString());


        // --------------------------
        StringBuilder resultService = new StringBuilder();
        resultService.append("package com.").append(dir.name).append(".service."+table.path+";\r\n\r\n");

        resultService.append("import com."+dir.name+".model."+table.path+"."+table.className+";\r\n");
        resultService.append("import com.github.pagehelper.Page;\r\n\r\n");

        resultService.append("public interface  "+table.className+"Service {\r\n\r\n");

        resultService.append("\t Page<"+table.className+"> find("+table.className+" search,int page,int pageSize);\r\n\r\n");
        resultService.append("\t "+table.className+" findById(Long id);\r\n\r\n");
        resultService.append("\t boolean save("+table.className+" model);\r\n\r\n");
        resultService.append("\t boolean update(Long id,"+table.className+" update);\r\n\r\n");

        resultService.append("}").append("\r\n");
        File service = new File(dir.java+"/service/"+table.path+"/"+table.className+"Service.java");
        wirteFile(service,resultService.toString());


        // --------------------------
        StringBuilder resultServiceImpl = new StringBuilder();
        resultServiceImpl.append("package com.").append(dir.name).append(".service."+table.path+".impl;\r\n\r\n");

        resultServiceImpl.append("import com."+dir.name+".model."+table.path+"."+table.className+";\r\n");
        resultServiceImpl.append("import com."+dir.name+".dao."+table.path+"."+table.className+"Mapper;\r\n");
        resultServiceImpl.append("import com."+dir.name+".service."+table.path+"."+table.className+"Service;\r\n");
        resultServiceImpl.append("import com."+dir.name+".service.BaseService;\r\n");
        resultServiceImpl.append("import org.springframework.beans.factory.annotation.Autowired;\r\n");
        resultServiceImpl.append("import org.springframework.stereotype.Service;\r\n");
        resultServiceImpl.append("import com.github.pagehelper.Page;\r\n\r\n");
        resultServiceImpl.append("import java.util.List;\r\n\r\n");

        resultServiceImpl.append("@Service\r\n");
        resultServiceImpl.append("public class  "+table.className+"ServiceImpl extends BaseService implements "+table.className+"Service {\r\n\r\n");
        String mapper  = table.className.substring(0,1).toLowerCase()+table.className.substring(1)+"Mapper";

        resultServiceImpl.append("\t @Autowired\r\n");
        resultServiceImpl.append("\t private "+table.className+"Mapper "+mapper+";\r\n\r\n\r\n");


        resultServiceImpl.append("\t @Override\r\n");
        resultServiceImpl.append("\t public Page<"+table.className+"> find("+table.className+" search,int page,int pageSize){\r\n");
        resultServiceImpl.append("\t\t this.setPage(page,pageSize);\r\n");
        resultServiceImpl.append("\t\t Page<"+table.className+"> result = (Page<"+table.className+">) "+mapper+".find(search);\r\n");
        resultServiceImpl.append("\t\t return result;\r\n");
        resultServiceImpl.append("\t}\r\n\r\n");

        resultServiceImpl.append("\t @Override\r\n");
        resultServiceImpl.append("\t public "+table.className+" findById(Long id){\r\n");
        resultServiceImpl.append("\t\t "+table.className+" search = new "+table.className+"();\r\n");
        resultServiceImpl.append("\t\t search.setId(id);\r\n");
        resultServiceImpl.append("\t\t List<"+table.className+"> result = "+mapper+".find(search);\r\n");
        resultServiceImpl.append("\t\t return result == null || result.size() == 0 ? null :result.get(0);\r\n");
        resultServiceImpl.append("\t}\r\n\r\n");

        resultServiceImpl.append("\t @Override\r\n");
        resultServiceImpl.append("\t public boolean save("+table.className+" model){\r\n");
        resultServiceImpl.append("\t\t int row = "+mapper+".save(model);\r\n");
        resultServiceImpl.append("\t\t return row > 0;\r\n");
        resultServiceImpl.append("\t }\r\n\r\n");

        resultServiceImpl.append("\t @Override\r\n");
        resultServiceImpl.append("\t public boolean update(Long id,"+table.className+" update){\r\n");
        resultServiceImpl.append("\t\t update.setId(id);\r\n");
        resultServiceImpl.append("\t\t int row = "+mapper+".update(update);\r\n");
        resultServiceImpl.append("\t\t return row > 0;\r\n");
        resultServiceImpl.append("\t }\r\n\r\n");
        resultServiceImpl.append("}").append("\r\n");
        File serviceImpl = new File(dir.java+"/service/"+table.path+"/impl/"+table.className+"ServiceImpl.java");
        wirteFile(serviceImpl,resultServiceImpl.toString());

    }

    private static void createMapperXml(Dir dir, Table table) throws Exception{
        StringBuilder result = new StringBuilder();
        result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
        result.append("<!DOCTYPE mapper  PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n");
        result.append("<mapper namespace=\"com."+dir.name+".dao."+table.path+"."+table.className+"Mapper\">\r\n");
        result.append("\t<resultMap id=\"BaseResultMap\" type=\"com."+dir.name+".model."+table.path+"."+table.className+"\">\r\n");
        result.append("\t\t<id column=\"id\" jdbcType=\"BIGINT\" property=\"id\" />\r\n");
        StringBuilder sqlColumn = new StringBuilder();
        sqlColumn.append("t.id,");
        if(table.column != null && table.column.length > 0){
            for(Column item : table.column){
                if(item.name.equals("id")){
                    continue;
                }
                String jdbcType = ALL_TYPE.get(item.type).mybaties;
                sqlColumn.append("t.").append(item.name).append(",");
                result.append("\t\t<result column=\""+item.name+"\" jdbcType=\""+jdbcType+"\" property=\""+columnToJavaFailed(item.name)+"\" />\r\n");
            }
            sqlColumn.deleteCharAt(sqlColumn.length()-1);
        }
        result.append("\t</resultMap>\r\n\r\n\r\n");
        result.append("\t<sql id=\"base_column\">\r\n");
        result.append("\t\t").append(sqlColumn).append("\r\n");
        result.append("\t</sql>\r\n\r\n\r\n");

        result.append("\t<select id=\"find\" parameterType=\"com."+dir.name+".model."+table.path+"."+table.className+"\" resultMap=\"BaseResultMap\">\r\n");
        result.append("\t\tSELECT <include refid=\"base_column\" /> FROM "+table.name+" t \r\n");
        result.append("\t\t<where>\r\n");
        result.append("\t\t\t<if test=\"id != null and id > 0 \">\r\n");
        result.append("\t\t\t\tAND t.id = #{id}\r\n");
        result.append("\t\t\t</if>\r\n");
        result.append("\t\t</where>\r\n");
        result.append("\t\t ORDER BY t.id DESC\r\n");
        result.append("\t</select>\r\n\r\n\r\n\r\n");

        result.append("\t<insert id=\"save\" useGeneratedKeys=\"true\" keyProperty=\"id\" parameterType=\"com."+dir.name+".model."+table.path+"."+table.className+"\">\r\n");
        result.append("\t\t INSERT INTO "+table.name+"(");
        if(table.column != null && table.column.length > 0){
            for(Column item : table.column){
                if(item.name.equals("id")){
                    continue;
                }
                result.append("`").append(item.name).append("`,");
            }
            result.deleteCharAt(result.length() - 1);
        }
        result.append(")\r\n\t\tVALUES (");
        if(table.column != null && table.column.length > 0){
            for(Column item : table.column){
                if(item.name.equals("id")){
                    continue;
                }
                if(item.name.equals("modify_date") || item.name.equals("create_date")){
                    result.append("now()").append(",");
                }else {
                    result.append("#{").append(columnToJavaFailed(item.name)).append("},");
                }
            }
            result.deleteCharAt(result.length() - 1);
        }
        result.append(")\r\n");
        result.append("\t</insert>\r\n\r\n\r\n");

        result.append("\t<update id=\"update\" parameterType=\"com."+dir.name+".model."+table.path+"."+table.className+"\">\r\n");
        result.append("\t\tUPDATE "+table.name+"\r\n");
        StringBuilder createSql = new StringBuilder();
        createSql.append("CREATE TABLE `"+table.name+"` (\r\n");
        createSql.append("`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '表主键',\r\n");
        if(table.column != null && table.column.length > 0){
            for(Column item : table.column){
                if(item.name.equals("id")){
                    continue;
                }
                if(!item.name.equals("create_date") && !item.name.equals("modify_date")){
                    String javaColumn  = columnToJavaFailed(item.name);
                    result.append("\t\t<if test=\""+javaColumn+" !=null\">\r\n");
                    result.append("\t\t\t`"+item.name+"` = #{"+javaColumn+"},\r\n");
                    result.append("\t\t</if>\r\n");
                }
                JavaToSql jts = ALL_TYPE.get(item.type);
                createSql.append("`" + item.name + "` " + jts.sql + jts.len + " DEFAULT NULL COMMENT '"+item.desc+"',\r\n");
            }
        }
        createSql.append("PRIMARY KEY (`id`)\r\n");
        createSql.append(") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;\r\n");

        result.append("\t\t\t`modify_date` = now() WHERE  id = #{id} \r\n");
        result.append("\t</update>\r\n\r\n</mapper>");

        File ja = new File(dir.sources+"/mapper/"+table.className+"Mapper.xml");
        wirteFile(ja,result.toString());
        File sql = new File(dir.sources+"/mapper/"+table.className+".sql");
        wirteFile(sql,createSql.toString());
    }


    private static void createModel(Dir dir,Table table) throws Exception{
        StringBuilder result = new StringBuilder();
        result.append("package com.").append(dir.name).append(".model."+table.path+";\r\n\r\n");
        result.append("import lombok.Data;\r\n\r\n");
        result.append("import java.util.Date;\r\n\r\n");
        result.append("@Data\r\n");
        result.append("public class "+table.className+" {\r\n\r\n");
        //result.append("private Long id;\r\n");
        if(table.column != null && table.column.length > 0){
            for(Column item : table.column){
                result.append("\tprivate ").append(item.type).append(" ").append(columnToJavaFailed(item.name)).append(";\r\n\r\n");
            }
        }
        result.append("}\r\n");
        File ja = new File(dir.java+"/model/"+table.path+"/"+table.className+".java");
        wirteFile(ja,result.toString());
    }



    private static String getFileTemplateUrl(String name){
        return "http://octk4wj3v.bkt.clouddn.com/porject-template/" + name;
    }

    private static String getFileTemplateValue(String _url) throws Exception{
        URL url =new URL(_url);
        HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
        urlCon.setConnectTimeout(5000);
        urlCon.setReadTimeout(5000);
        urlCon.setRequestProperty("Content-Type","text/plain;charset=gbk");
        urlCon.setRequestProperty("Charset", "utf-8");
        BufferedReader br =new BufferedReader(new InputStreamReader( urlCon.getInputStream(),"gbk"));
        String  read = null;
        StringBuilder strBuilder = new StringBuilder();
        while ((read = br.readLine()) !=null) {
            strBuilder.append(read).append("\r\n");
        }
        br.close();
        return strBuilder.toString();
    }

    private static String replaseAll(String str,Placeholder placeholder){
        if(str == null || str.length() == 0){
            return "";
        }
        int index = -1;
        String subStr = str;
        StringBuilder result = new StringBuilder();
        while((index = subStr.indexOf("${"+placeholder.key)) != -1){
            result.append(subStr.substring(0,index)).append(placeholder.value);
            subStr = subStr.substring(index + placeholder.key.length() + 3);
        }
        result.append(subStr);
        return result.toString();
    }


    private static String columnToJavaFailed(String column){

        String [] items = column.split("_");
        if(items.length <= 1){
            return column;
        }
        StringBuilder result = new StringBuilder();
        result.append(items[0]);
        for(int i = 1; i < items.length; i ++){
            String tmp = items[i];
            result.append(tmp.substring(0,1).toUpperCase())
                    .append(tmp.substring(1));
        }
        return result.toString();
    }

}